package com.yls.autoapply.server;

import com.yls.autoapply.model.SshConnection;
import com.yls.autoapply.exception.SshRunException;
import com.yls.autoapply.exception.SshTimeoutException;
import com.yls.autoapply.model.SshResponse;
import com.yls.autoapply.util.CmdUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.channel.ChannelExec;
import org.apache.sshd.client.channel.ClientChannelEvent;
import org.apache.sshd.client.future.ConnectFuture;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.sftp.client.SftpClient;
import org.apache.sshd.sftp.client.SftpClientFactory;
import org.apache.sshd.sftp.client.fs.SftpFileSystem;
import org.springframework.util.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Slf4j
public abstract class ServerAbs {

    private SshClient client = SshClient.setUpDefaultClient();

    private ClientSession session;

    private SftpClient sftpClient;

    private SftpFileSystem fs;

    private SshConnection connection;

    public ServerAbs() {}

    public static void main(String[] args) throws InterruptedException {
        String deploy = "kubectl get deployment -o yaml -n %s | grep -E  \"image:.*:.*\" | awk '!a[$0]++{print}'";
        System.out.println(String.format(deploy, "qz4auat1"));

        long start = System.currentTimeMillis();
        //Thread.sleep(5000);
        long end = System.currentTimeMillis();

        System.out.println(end-start);

        System.out.println(String.format(CmdUtil.tag, "source", "dest"));

    }

    public void auth(SshConnection conn) throws IOException{
        connection = conn;
        try {
            // Open the client
            client.start();

            // Connect to the server
            ConnectFuture cf = client.connect(conn.getUser(), conn.getIp(), conn.getPort());
            session = cf.verify().getSession();
            session.addPasswordIdentity(conn.getPwd());
            session.auth().verify(TimeUnit.SECONDS.toMillis(conn.getTimeout()));

            sftpClient = SftpClientFactory.instance().createSftpClient(session);
            fs = SftpClientFactory.instance().createSftpFileSystem(session);

        } catch (IOException e) {
            log.error("" + e);
            client.stop();
            throw e;
        }
    }

    protected SshResponse runCmd(String cmd) throws IOException, SshTimeoutException, SshRunException {
        // Create the exec and channel its output/error streams
        ChannelExec ce = this.getSession().createExecChannel(cmd);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayOutputStream err = new ByteArrayOutputStream();
        ce.setOut(out);
        ce.setErr(err);

//       Execute and wait
        ce.open();
        Set<ClientChannelEvent> events =
                ce.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), TimeUnit.SECONDS.toMillis(getConnection().getTimeout()));

//       Check if timed out
        if (events.contains(ClientChannelEvent.TIMEOUT)) {
            throw new SshTimeoutException(cmd, getConnection().getIp(), getConnection().getTimeout());
        }

        //执行命令失败
        if (!StringUtils.isEmpty(err.toString())) {
            throw new SshRunException(cmd, getConnection().getIp(), err.toString());
        }

        return new SshResponse(out.toString(), err.toString(), ce.getExitStatus());
    }

    public boolean rm(String path) throws IOException {
        Path resolve = getFs().getDefaultDir().resolve(path);
        //删非空目录会报错
        return Files.deleteIfExists(resolve);
    }

    public SshResponse rmdir(String path) throws IOException, SshRunException, SshTimeoutException {
        Path resolve = getFs().getDefaultDir().resolve(path);
        if (Files.exists(resolve)) {
            return this.runCmd(String.format(CmdUtil.rmdir, path));
        }
        return null;
    }

    public void upload(String remoteDir, String remoteFile, InputStream in) throws IOException {
        Path remote = fs.getDefaultDir().resolve(remoteDir);
        Files.createDirectories(remote);
        this.rm(remoteDir + remoteFile);
        SftpClient.CloseableHandle handle = sftpClient.open(remoteDir + remoteFile, SftpClient.OpenMode.Write, SftpClient.OpenMode.Create);
        int buff_size = 1024*1024*20;
        byte[] src = new byte[buff_size];
        int len;
        long fileOffset = 0L;
        long start = System.currentTimeMillis();
        while ((len = in.read(src)) != -1) {
            sftpClient.write(handle, fileOffset, src, 0, len);
            fileOffset += len;
        }
        long end = System.currentTimeMillis();
        log.info(String.valueOf((end - start) / 1000));
    }

    public abstract void close() throws IOException;

    protected SshClient getClient() {return this.client; }

    protected ClientSession getSession() {return this.session;}

    protected SftpClient getSftpClient() {return this.sftpClient;}

    protected SftpFileSystem getFs() {return this.fs;}

    protected SshConnection getConnection() {return this.connection;}
}
